specs(exact): propose TON exact scheme for x402 v2 (spec-only)#1455
specs(exact): propose TON exact scheme for x402 v2 (spec-only)#1455ohld wants to merge 19 commits intox402-foundation:mainfrom
Conversation
🟡 Heimdall Review Status
|
|
@ohld is attempting to deploy a commit to the Coinbase Team on Vercel. A member of the Team first needs to authorize it. |
fbf520a to
86c1427
Compare
|
Really excited to see TON getting a proper x402 spec — the W5/ 1. 2. 3. Relay commission amount is undefined |
|
Thanks for the thorough review, these are all great catches. 1. You're right — 2. Good call. I didn't want to hardcode a specific hash in the spec since W5 has revisions (v5r1, potentially v5r2) and a hardcoded hash would become outdated when TON Foundation ships a new version. Instead I added: facilitator MUST verify that the contract code in 3. Relay commission bounds Added Also tightened rule §4: the W5 message must contain exactly two actions (payment transfer + optional commission transfer), anything else is rejected. All changes in the latest commit + updated the demo repo to enforce exact amount matching (30/30 tests passing). |
a418dd0 to
9f6683f
Compare
|
Thanks a lot for putting this together @ohld! Notified the core team to review. Please be aware that we currently have a lot of pending/incoming requests, so it might take a bit until we can get back to you, thanks for your patience 🙏 |
|
While we wait for the spec review, I created a facilitator for TON https://github.com/ohld/x402-ton-facilitator And our community also implemented one: https://github.com/TONresistor/x402-ton We'll start implementing all SDKs as well. |
Add @x402/tvm (TypeScript) and x402[tvm] (Python) mechanism packages implementing the exact payment scheme for TON blockchain. Python (mechanisms/tvm/): - Full gasless USDT payment flow via TONAPI relay - Ed25519 signature verification for W5R1 wallets - BoC parser for external messages, jetton transfers - 6-rule payment verification (protocol, signature, intent, replay, relay safety, simulation) - Idempotent settlement with state machine - 72 unit tests TypeScript (@x402/tvm): - SchemeNetworkClient/Server/Facilitator implementations - W5R1 wallet signing with @ton/ton SDK - Gasless estimate + settlement via TONAPI - CAIP-2 network IDs: tvm:-239 (mainnet), tvm:-3 (testnet) - 48 unit tests Refs: spec PR x402-foundation#1455, live facilitator at ton-facilitator.okhlopkov.com
|
We implemented this spec in production with x402-ton (https://github.com/TONresistor/x402-ton). Two findings from our integration: Commission discovery at boot: The spec recommends discovering commission via Native TON support: Our implementation supports both USDT gasless (internal auth via TONAPI) and native TON direct broadcast (external auth). The PR describes the jetton path only. We treat native TON as an additive superset since payers with TON already have gas and don't need the gasless relay. Live facilitator: https://x402.resistance.dog |
|
|
||
| - `payload.seqno` MUST match the wallet's current on-chain seqno. | ||
| - Duplicate `signedBoc` submissions MUST be rejected. | ||
| - The W5 message MUST contain exactly two actions: the payment `jetton_transfer` and (optionally) a relay commission `jetton_transfer`. Any additional actions MUST cause rejection. |
There was a problem hiding this comment.
| - The W5 message MUST contain exactly two actions: the payment `jetton_transfer` and (optionally) a relay commission `jetton_transfer`. Any additional actions MUST cause rejection. | |
| - The W5 message MUST contain at most two actions: the payment `jetton_transfer` and (optionally) a relay commission `jetton_transfer`. Any additional actions MUST cause rejection. |
| - `payTo`: Recipient TON address (raw format). | ||
| - `amount`: Atomic token amount (6 decimals for USDT, so `10000` = $0.01). | ||
| - `extra.relayAddress`: (Optional) Gasless relay address that receives the relay commission. When present, the W5 batch includes a separate `jetton_transfer` to this address as commission for gas sponsorship. When absent, the client handles gas fees directly. | ||
| - `extra.maxRelayCommission`: (Optional) Maximum relay commission in atomic token units. When present, facilitator MUST reject W5 batches where the commission transfer exceeds this amount. When absent, the facilitator applies its own commission policy. |
There was a problem hiding this comment.
Who effectively pays the relayFee in the end? Is the client sending amount + relayFee or is the server receiving amount - relayFee?
| - `asset`: [TEP-74] Jetton master contract address (raw format `workchain:hex`). | ||
| - `payTo`: Recipient TON address (raw format). | ||
| - `amount`: Atomic token amount (6 decimals for USDT, so `10000` = $0.01). | ||
| - `extra.relayAddress`: (Optional) Gasless relay address that receives the relay commission. When present, the W5 batch includes a separate `jetton_transfer` to this address as commission for gas sponsorship. When absent, the client handles gas fees directly. |
There was a problem hiding this comment.
Could you please clarify if the facilitator acts as relayer or is the relayer an independent 3rd party actor?
There was a problem hiding this comment.
If its a a 3rd party, how does the server know to which relay service the facilitator will submit the tx? Ie how to get relayAddress?
|
|
||
| The `exact` scheme on TON transfers a specific amount of a [TEP-74] Jetton from the client to the resource server using a W5 wallet `internal_signed` message. | ||
|
|
||
| The facilitator sponsors gas by wrapping the client-signed message in an internal TON message. The client controls payment intent (asset, recipient, amount) through Ed25519 signature. The facilitator cannot modify the destination or amount. |
There was a problem hiding this comment.
From reading through the specs, it is my understanding that the client would sign two batched transfers to payTo and relayAddress. The facilitator wouldn't actually sponsor gas? The facilitator would effectively just pass through the client request to the relayer?
If this is the case, I think phrasing the relayer service as gas sponsorship is also a bit misleading. Its more like a mechanism for the client to pay for gas in a non-native token. But the client still effectively pays for the gas
|
|
||
| 1. Re-run all verification checks (do not trust prior `/verify` result). | ||
| 2. Submit `signedBoc` via gasless relay or direct broadcast: | ||
| - **Sponsored (gasless):** `POST /v2/gasless/send` with `{ wallet_public_key, boc }` to a relay service. The relay wraps the signed message in an internal message carrying TON for gas. |
There was a problem hiding this comment.
This sounds like relayer is an independent service
|
|
||
| - The facilitator/relay account MUST NOT appear as the source of the Jetton transfer. | ||
| - The facilitator MUST NOT be the payer (`walletAddress`) for the delegated transfer. | ||
| - The facilitator address MUST NOT appear as destination in any `jetton_transfer` within the W5 message (except for the relay commission transfer to `extra.relayAddress`). |
There was a problem hiding this comment.
This sounds like facilitator == relayer
| ### 4. Replay and anti-abuse checks | ||
|
|
||
| - `payload.seqno` MUST match the wallet's current on-chain seqno. | ||
| - Duplicate `signedBoc` submissions MUST be rejected. |
There was a problem hiding this comment.
This needs to be discussed in much more detail. Could you please elaborate why this is needed and how this would work in practice? Would this imply a stateful facilitator?
x402 is designed as a stateless protocol. We recently made one exception for SVM to patch a security vulnerability, see https://github.com/coinbase/x402/blob/main/specs/schemes/exact/scheme_exact_svm.md#duplicate-settlement-mitigation-recommended.
If TON could use a similar approach as SVM with a short-term, in-memory cache of transaction payloads, that would be acceptable but should be well justified in a dedicated section of the spec.
If persistent storage would be needed, this would warrant a deeper discussion with the core team
|
Hi @ohld, thanks again for your contribution! This a great start and the spec is well written and structured. Please have a look at my comments above, there a 2 main points to clarify:
|
|
Would it be possible to let the facilitator actually sponsor the gas aligning the flow with the existing mechanism for other chains? Concretely I would suggest the following (if technical possible) that would significantly simplify the spec:
|
|
Updated spec to self-relay architecture. Tested with 7 mainnet USDT payments. Key change: Facilitator = relay. No commission. Client signs 1 jetton_transfer, Review comments:
cc @nicholasrice — you've been working on TON support too (#1495) |
|
One thing worth flagging: TON gas is For now my facilitator covers gas from personal funds, same as the free facilitators on other chains. But for production sustainability, facilitators would likely either bill merchants out-of-band (the standard x402 model) or include an optional commission transfer in the Worth noting: TON Foundation could sponsor a public facilitator to bootstrap adoption (similar to Coinbase's CDP facilitator on Base). And realistically, TON x402 makes more sense for $0.10+ payments where gas overhead is proportional. None of this changes the spec — just providing context on operational economics. |
Changes: - Remove /prepare endpoint: client resolves seqno + jetton wallet via TON RPC directly (same pattern as SVM/Stellar/Aptos) - Remove nonce field: dedup uses BoC hash, not nonce - Transfer amount: strict equality (== not >=) - Add jetton wallet verification: recipient MUST match get_wallet_address(payTo) on the asset master - Seqno check: MUST NOT be less than on-chain seqno (was SHOULD) - Add client balance check requirement - Move simulation to /verify (was optional pre-settlement) - Remove HTTP header references (transport-agnostic) - Replace hardcoded 600s with maxTimeoutSeconds - Add Client RPC Requirements appendix section
- Add section 3 (Facilitator Safety): facilitator address MUST NOT appear as sender or Jetton transfer source. Matches SVM/Stellar. - Make get_wallet_address check explicit per @skywardboundd: the destination in the deserialized BoC must match the resolved wallet. - Align verification intro phrasing with SVM/Stellar style.
Follows Stellar's approach: extra.areFeesSponsored boolean, currently always true. Non-sponsored flow (client pays gas) to be added later.
… check - Remove duplicate validUntil check from section 2 (already in section 5) - Add stateInit code hash verification for seqno==0 deployments (per arjun215-eng's earlier feedback)
Per @skywardboundd review: 1. payload.to MUST equal requirements.payTo (explicit metadata check) 2. Source Jetton wallet (W5 internal msg destination) MUST match get_wallet_address(from) — prevents substitute source contract 3. jetton_transfer body destination MUST equal requirements.payTo Tested against deployed facilitator with real on-chain data. Facilitator commit: ohld/x402-ton-facilitator@6f92320
… W5R1 hash - Remove facilitatorUrl from extra (implementation detail, not protocol field) - Fix dedup TTL: use fixed 300s instead of per-request maxTimeoutSeconds - Add canonical W5R1 code hash for stateInit verification - Note simulation as alternative to code hash checks for seqno>0 Per @phdargen review on coinbase/x402#1455
Major rework based on TON Core team review:
- settlementBoc is now an internal message (not external)
- Payload reduced to {settlementBoc, asset}, all fields derived from BoC
- Public key derived from stateInit or on-chain get_public_key
- Seqno check changed to strict equality (TON requirement)
- Internal message must be bounceable
- Removed facilitatorUrl, nonce, walletPublicKey
- Clarified simulation in /verify, extensibility for non-gasless flows
- Updated parent scheme_exact.md TON section
…tion, simulation chain - Replace seqno==0 with proper TON account states (nonexist/uninit) for stateInit condition - Clarify that payload.asset is a JSON field, not a BoC-internal field (TEP-74 has no asset) - Add full TEP-74 transfer chain verification to simulation section - Remove non-normative timing note from settlement step 7 - Add W5 gasless transactions reference link
- Narrow wallet scope to W5 v5r1 (not v5r1+), only version that exists today - Remove redundant "x402 protocol-level requirement" sentence - Add get_jetton_data hint for decimal resolution - Add walletId to derived fields list - Rename section 2 to "Message and signature verification" - Move balance check from Replay protection to Payment intent - Remove redundant seqno explanation sentence
2dac972 to
7af4477
Compare
Refine the TON `exact` scheme spec to better describe how client-signed W5 jetton transfers are verified and relayed by the facilitator. This update expands the TON payment requirements and verification rules to account for optional jetton transfer parameters exposed through `extra`, and clarifies how those parameters are interpreted when they are omitted. Changes included: - add optional `extra.forwardPayload` and `extra.forwardTonAmount` to TON `PaymentRequirements` and define their effective defaults as zero-bit payload and `"0"` - add optional `extra.responseDestination` and define its effective default as `addr_none` - require facilitators to compare the effective values of `responseDestination`, `forwardPayload`, and `forwardTonAmount` between `accepted.extra` and `requirements.extra` - extend payment intent validation so the facilitator derives and checks `response_destination`, `forward_payload`, and `forward_ton_amount` from the signed W5 action - clarify that the facilitator-funded relay must cover both outer relay execution and the value expected by the client-signed inner transfer - clarify that the client-signed inner message must carry enough TON to fund payer-side execution from the payer jetton wallet to the source jetton wallet - specify that the outbound internal message from the client W5 wallet to the source jetton wallet must be bounceable - specify that the `settlementBoc` wrapper itself does not need to be bounceable - clean up terminology and consistency across the document: `BoC`, `zero-bit`, `forwardPayload`, `forwardTonAmount`, and derived field descriptions This is a spec clarification/update only. It does not introduce a new TON payment scheme, but makes the existing gas-sponsored W5 relay flow more precise.
7af4477 to
69fca0a
Compare
|
Hey, @phdargen! We have updated our commits to be verified |
Description
Adds formal specification for the
exactpayment scheme on TON blockchain, following the same structure as existing network-specific scheme documents (EVM, SVM, Stellar, Aptos).specs/schemes/exact/scheme_exact_ton.md-- full spec for TON exact schemespecs/schemes/exact/scheme_exact.md-- adds TON validation rules to the indexTests
No code affected.
Checklist
Why TON
Working proof
Key design decisions
v2 alignment
Review request
Please review the scheme design, payload shape, and verification/settlement safety constraints. Once aligned, I will open a separate implementation PR in TypeScript.